/*
* 在802.11be中启动mimo
* 现在来看ns-3作为一个网络仿真器，只能观察出网络的性能，而非wifi芯片运行的细节；
* 
* 下面是一些个人想法，可能有误 2024/2/29/15：20
* 按照example文件wifi-80211n-mimo.cc中的写法，mimo的多stream是工作于同一个channel之中；
* 那这样的话，除了波束成形，提高对特定方向的传输距离这个特点外，似乎并不会有效的增加带宽，包括AP向多个STA传输的下行链路带宽；
* 直观上讲，还不如OFDMA的 multiuser 来的直观*/
#include "ns3/boolean.h"
#include "ns3/callback.h"
#include "ns3/command-line.h"
#include "ns3/config.h"
#include "ns3/double.h"
#include "ns3/eht-phy.h"
#include "ns3/enum.h"
#include "ns3/frame-exchange-manager.h"
#include "ns3/internet-stack-helper.h"
#include "ns3/ipv4-address-generator.h"
#include "ns3/ipv4-address-helper.h"
#include "ns3/ipv4.h"
#include "ns3/log.h"
#include "ns3/mobility-helper.h"
#include "ns3/multi-model-spectrum-channel.h"
#include "ns3/on-off-helper.h"
#include "ns3/packet-sink-helper.h"
#include "ns3/packet-sink.h"
#include "ns3/rng-seed-manager.h"
#include "ns3/spectrum-wifi-helper.h"
#include "ns3/ssid.h"
#include "ns3/string.h"
#include "ns3/udp-client-server-helper.h"
#include "ns3/uinteger.h"
#include "ns3/wifi-acknowledgment.h"
#include "ns3/wifi-mac.h"
#include "ns3/wifi-net-device.h"
#include "ns3/wifi-phy-state-helper.h"
#include "ns3/wifi-phy.h"

#include <array>
#include <chrono>
#include <functional>
#include <iomanip>
#include <iostream>
#include <numeric>
#include <sstream>

using namespace ns3;

int
main(int argc, char* argv[])
{
    NodeContainer wifiApNode;
    wifiApNode.Create(1);

    NodeContainer wifiStaNode;
    wifiStaNode.Create(1);

    WifiHelper wifiHelper;
    wifiHelper.SetStandard(WIFI_STANDARD_80211be);

    // 5GHz, mcs=0
    uint16_t nonHtRefRateMbps = EhtPhy::GetNonHtReferenceRate(0) / 1e6;
    std::string ctrlRateStr = "OfdmRate" + std::to_string(nonHtRefRateMbps) + "Mbps";
    uint8_t nLinks = 0;
    wifiHelper.SetRemoteStationManager(nLinks, // 这里就很疑惑啊，为什么我使用1和使用0会调用不同的函数？
                                       "ns3::ConstantRateWifiManager",
                                       "DataMode",
                                       StringValue("EhtMcs0"),
                                       "ControlMode",
                                       StringValue(ctrlRateStr));

    Ssid ssid = Ssid("wifi-80211be-mimo");

    // link = 1
    SpectrumWifiPhyHelper phy(1);
    phy.SetPcapDataLinkType(
        WifiPhyHelper::DLT_IEEE802_11_RADIO); // 之前写仿真文件的时候好像并没有这样初始化pcap
    phy.Set("ChannelSwitchDelay",
            TimeValue(MicroSeconds(100))); // 100ms的信道切换延迟，虽然不知道有什么作用

    WifiMacHelper macHelper;
    macHelper.SetType("ns3::StaWifiMac", "Ssid", SsidValue(ssid));

    phy.Set((uint8_t)0, "ChannelSettings", StringValue("{0, 20, BAND_5GHZ, 0}"));
    // Set MIMO capabilities
    uint8_t nStreams = 4;
    phy.Set("Antennas", UintegerValue(nStreams));
    phy.Set("MaxSupportedTxSpatialStreams", UintegerValue(nStreams));
    phy.Set("MaxSupportedRxSpatialStreams", UintegerValue(nStreams));
    auto spectrumChannel = CreateObject<MultiModelSpectrumChannel>();
    auto lossModel = CreateObject<LogDistancePropagationLossModel>();
    spectrumChannel->AddPropagationLossModel(lossModel);
    phy.AddChannel(spectrumChannel, WIFI_SPECTRUM_5_GHZ);

    NetDeviceContainer staDevice = wifiHelper.Install(phy, macHelper, wifiStaNode);

    // 对于支持802.11be的AP，直接安装支持multiUser的调度器
    macHelper.SetMultiUserScheduler("ns3::RrMultiUserScheduler");

    macHelper.SetType("ns3::ApWifiMac",
                      "EnableBeaconJitter",
                      BooleanValue(false),
                      "Ssid",
                      SsidValue(ssid));
    NetDeviceContainer apDevice = wifiHelper.Install(phy, macHelper, wifiApNode);

    RngSeedManager::SetSeed(1);
    RngSeedManager::SetRun(1);
    int64_t streamNumber = 100;
    streamNumber += wifiHelper.AssignStreams(apDevice, streamNumber);
    streamNumber += wifiHelper.AssignStreams(staDevice, streamNumber);

    int gi = 3200; // ns
                   // Set guard interval and MPDU buffer size
    Config::Set("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/HeConfiguration/GuardInterval",
                TimeValue(NanoSeconds(gi)));
    Config::Set("/NodeList/*/DeviceList/*/$ns3::WifiNetDevice/HeConfiguration/MpduBufferSize",
                UintegerValue(256));

    double distance = 10; // mimo最大的特色应该就是传输距离的增加
    MobilityHelper mobility;
    Ptr<ListPositionAllocator> positionAlloc = CreateObject<ListPositionAllocator>();

    positionAlloc->Add(Vector(0.0, 0.0, 0.0));
    positionAlloc->Add(Vector(distance, 0.0, 0.0));
    mobility.SetPositionAllocator(positionAlloc);

    mobility.SetMobilityModel("ns3::ConstantPositionMobilityModel");

    mobility.Install(wifiApNode);
    mobility.Install(wifiStaNode);

    // 传输层和网络层协议栈, simulationTime = 3s;
    double simulationTime = 3;
    InternetStackHelper stack;
    stack.Install(wifiApNode);
    stack.Install(wifiStaNode);

    Ipv4AddressHelper address;
    address.SetBase("192.168.1.0", "255.255.255.0");
    Ipv4InterfaceContainer staNodeInterface = address.Assign(staDevice);
    Ipv4InterfaceContainer apNodeInterface = address.Assign(apDevice);

    // udp port = 9;
    uint16_t port = 9;
    UdpServerHelper server(port);
    ApplicationContainer serverApp = server.Install(wifiApNode);
    UdpClientHelper client(apNodeInterface.GetAddress(0), port);
    client.SetAttribute("MaxPackets", UintegerValue(4294967295U));
    client.SetAttribute("Interval", TimeValue(Time("0.00001"))); // packets/s
    client.SetAttribute("PacketSize", UintegerValue(700));
    ApplicationContainer clientApp = client.Install(wifiStaNode);
    serverApp.Start(Seconds(1.0));
    serverApp.Stop(Seconds(simulationTime + 1));
    clientApp.Start(Seconds(1.0));
    clientApp.Stop(Seconds(simulationTime + 1));

    phy.EnablePcap("wifi-80211be-mimo-ap", wifiApNode, true);
    phy.EnablePcap("wifi-80211be-mimo-sta", wifiStaNode, true);
    Simulator::Stop(Seconds(simulationTime + 1));
    Simulator::Run();

    Simulator::Destroy();

    return 0;
}